#pragma once
#include "../mqcommon/Helper.hpp"
#include <mutex>
#include <memory>
#include <google/protobuf/map.h>
namespace mq_zxws
{
    struct MsgQueue{
        using ptr = std::shared_ptr<MsgQueue>;
        std::string name;
        bool durable;
        bool exclusive;
        bool auto_delete;
        google::protobuf::Map<std::string, std::string> args;
        MsgQueue(){}
        MsgQueue(const std::string& qname,
                bool qdurable,
                bool qexclusive,
                bool qauto_delete,
                const google::protobuf::Map<std::string, std::string>& qargs)
            :name(qname), durable(qdurable), exclusive(qexclusive), auto_delete(qauto_delete), args(qargs)
        {}
        void setArgs(const std::string & str_args){
            std::vector<std::string> sub_str;
            StrHelper::splite(str_args, "&", sub_str);
            for(const auto& str:sub_str){
                size_t pos = str.find('=');
                std::string key = str.substr(0, pos);
                std::string val = str.substr(pos + 1);
                args[key] = val;
            }
        }
        // 将 args 中的数据变成字符串 key=val&key=val...
        std::string getArgs(){
            std::string result;
            for(auto it = args.begin(); it != args.end(); ++it){
                result += it->first + "=" + it->second + "&";
            }
            return result;
        }
    };
    using QueueMap = std::unordered_map<std::string, MsgQueue::ptr>;
    class MsgQueueMapper{
    public:
        MsgQueueMapper(const std::string& dbfile)
            :_sql_helper(dbfile)
        {
            std::string parent = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(parent);
            assert(_sql_helper.open());
            createTable();
        }
        void createTable()
        {
            std::string sql = "create table if not exists queue_table(\
                                name varchar(32) primary key,\
                                durable int,\
                                exclusive int,\
                                auto_delete int,\
                                args varchar(128));";
            assert(_sql_helper.exec(sql, nullptr, nullptr));
        }
        void removeTable(){
            std::string sql = "drop table if exists queue_table;";
            _sql_helper.exec(sql, nullptr, nullptr);
        }
        bool insert(MsgQueue::ptr & queue){
            char sql[4096]{};
            sprintf(sql, "insert into queue_table values('%s', %d, %d, %d, '%s');",
                    (queue->name).c_str(), queue->durable, queue->exclusive, queue->auto_delete, (queue->getArgs()).c_str());
            return _sql_helper.exec(sql, nullptr, nullptr);
        }
        bool remove(const std::string& name){
            std::string sql = "delete from queue_table where name = '" + name + "';";
            return _sql_helper.exec(sql, nullptr, nullptr);
        }
        
        QueueMap recovery(){
            std::string sql = "select * from queue_table;";
            QueueMap res;
            _sql_helper.exec(sql, SelectCallback, &res);
            return res;
        }
    private:
        static int SelectCallback(void *args, int col, char ** row, char ** col_name){
            QueueMap* res = static_cast<QueueMap*>(args);
            auto mqp = std::make_shared<MsgQueue>();
            mqp->name = row[0];
            mqp->durable = (bool)std::stoi(row[1]);
            mqp->exclusive = (bool)std::stoi(row[2]);
            mqp->auto_delete = (bool)std::stoi(row[3]);;
            if(row[4]) mqp->setArgs(row[4]);
            res->insert(std::make_pair(mqp->name, mqp));
            return 0;
        }
        SqliteHelper _sql_helper;
    };
    class MsgQueueManager{
    public:
        using ptr = std::shared_ptr<MsgQueueManager>;
        MsgQueueManager(const std::string& dbfile):_mapper(dbfile){
            _msg_queues = _mapper.recovery();
        }
        // 声明队列
        bool declareQueue(const std::string& name,
                bool durable,
                bool exclusive,
                bool auto_delete,
                const google::protobuf::Map<std::string, std::string>& args)
        {
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it == _msg_queues.end()){
                auto mqp = std::make_shared<MsgQueue>(name, durable, exclusive, auto_delete, args);
                if(durable) {// 需要持久化的数据才需要持久化操作
                    if(!_mapper.insert(mqp))
                        return false;
                }
                _msg_queues.insert(std::make_pair(name, mqp));
            }
            return true;
        }
        // 删除队列
        void deleteQueue(const std::string & name){
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it == _msg_queues.end()) return;
            _msg_queues.erase(it);
            if(it->second->durable) _mapper.remove(name); // 被持久化的数据才能被删除
        }
        // 获取指定队列接口
        MsgQueue::ptr selectOne(const std::string & name){
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if(it == _msg_queues.end()) return nullptr;
            return it->second;
        }
        QueueMap AllQueues(){
            std::lock_guard<std::mutex> lock(_mutex);
            return _msg_queues;
        }
        // 队列是否存在
        bool exists(const std::string& name){
            std::lock_guard<std::mutex> lock(_mutex);
            return _msg_queues.find(name) != _msg_queues.end();
        }
        // 清空队列
        void clear(){
            std::lock_guard<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _msg_queues.clear();
        }
        size_t size(){
            std::lock_guard<std::mutex> lock(_mutex);
            return _msg_queues.size();
        }
    private:
        std::mutex _mutex;
        MsgQueueMapper _mapper;
        QueueMap _msg_queues;
    };
} // namespace mq_zxws
